package com.smcaiot.filesync.starter.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.smcaiot.filesync.starter.entity.Base64Param;
import com.smcaiot.filesync.starter.service.IMyMinioService;
import com.smcaiot.filesync.starter.util.FileUtils;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.Objects;

@Service
@Slf4j
public class MyMinioService implements IMyMinioService {

    @Autowired(required = false)
    private MinioClient minioClient;

    @Override
    public boolean makeBucket(String bucketName) throws Exception {
        // 如存储桶不存在，创建之。
        boolean found = bucketExists(bucketName);
        if (found) {
            log.debug("{} already exists", bucketName);
            return false;
        } else {
            // 创建名为'my-bucketname'的存储桶。
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            log.debug("{} is created successfully", bucketName);
            return true;
        }
    }

    @Override
    public boolean bucketExists(String bucketName) throws Exception {
        // 检查'my-bucketname'是否存在。
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    @Override
    public InputStream getObject(String bucketName, String objectName, long offset) throws Exception {
        InputStream stream;
        try {
            // 获取"myobject"的输入流。
            stream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());

            return stream;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    @Override
    public boolean putObject(String bucketName, String objectName, InputStream stream, String contentType) throws Exception {
        try (InputStream fis = stream){
            PutObjectArgs.Builder builder = PutObjectArgs.builder().bucket(bucketName).object(objectName)
                    .stream(fis, -1, 10485760);
            if (StrUtil.isNotBlank(contentType)) {
                builder.contentType(contentType);
            }
            makeBucket(bucketName);
            minioClient.putObject(builder.build());
            log.debug("myobject is uploaded successfully");
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String uploadMinioFile(String localTmpPath, String destPath, boolean nameWithTime) {
        if (StrUtil.isBlank(localTmpPath) || StrUtil.isBlank(destPath)) {
            log.debug("上传minio文件: localTmpPath: {}, destPath: {}", localTmpPath, destPath);
            return null;
        }
        String bucketName = FileUtils.getRootByPath(destPath);
        String objectName = FileUtils.exceptRootByPath(destPath);
        if (nameWithTime) {
            objectName = FileUtils.fileNameWithTime(objectName);
        }
        log.debug("[上传文件], bucketName = {}, objectName = {}", bucketName, objectName);
        try (InputStream fileStream = new FileInputStream(localTmpPath)) {
            boolean ret = putObject(bucketName, objectName, fileStream, null);
            if (!ret) {
                log.warn("[上传失败] bucketName = {}, objectName = {}, available = {}", bucketName, objectName, fileStream.available());
                return null;
            }
        } catch (Exception e) {
            log.warn("上传minio文件失败：localTmpPath: {}, destPath: {}, err: {}", localTmpPath, destPath, e);
            return null;
        } finally {
            deleteFile(localTmpPath);
        }
        return new StringBuilder().append(StrUtil.C_SLASH).append(bucketName).append(objectName).toString();
    }

    @Override
    public void getMinioFile(String srcPath, String destPath) {
        if (StrUtil.isBlank(srcPath) || StrUtil.isBlank(destPath)) {
            log.debug("获取minio文件: srcPath: {}, destPath: {}", srcPath, destPath);
            return;
        }
        String bucketName = FileUtils.getRootByPath(srcPath);
        String objectName = FileUtils.exceptRootByPath(srcPath);
        if (StrUtil.isBlank(srcPath) || StrUtil.isBlank(destPath)) {
            log.debug("获取minio文件: srcPath: {}, destPath: {}", srcPath, destPath);
            return;
        }
        try (InputStream fis = getObject(bucketName, objectName, 0)) {
            byte[] bytes = IoUtil.readBytes(fis);
            try (FileOutputStream output = new FileOutputStream(destPath)) {
                IoUtil.write(output, true, bytes);
            }
        } catch (Exception e) {
            log.warn("获取minio文件失败: srcPath: {}, destPath: {}, err: {}", srcPath, destPath, e);
        }
    }

    @Override
    public InputStream getMinioFile(String srcPath) {
        if (StrUtil.isBlank(srcPath)) {
            log.debug("获取minio文件流: srcPath: {}", srcPath);
            return null;
        }
        String bucketName = FileUtils.getRootByPath(srcPath);
        String objectName = FileUtils.exceptRootByPath(srcPath);
        if (StrUtil.isBlank(srcPath)) {
            log.debug("获取minio文件流: srcPath: {}", srcPath);
            return null;
        }
        try {
            return getObject(bucketName, objectName, 0);
        } catch (Exception e) {
            log.warn("获取minio文件流失败: srcPath: {}, err: {}", srcPath, e);
            return null;
        }
    }

    private void deleteFile(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
    }

    @Override
    public String getFileBase64(String srcPath) {
        try (InputStream fis = getMinioFile(srcPath)) {
            if (Objects.isNull(fis)) {
                return null;
            }
            return Base64.getEncoder().encodeToString(IoUtil.readBytes(fis));
        } catch (Exception e) {
            log.warn("获取minio文件流失败: srcPath: {}, err: {}", srcPath, e);
            return null;
        }
    }

    @Override
    public boolean base64StoreFile(String base64, String bucketName, String objectName) throws Exception {
        InputStream is = FileUtils.baseToInputStream(base64);
        return putObject(bucketName, objectName, is, null);
    }

    @Override
    public String base64StoreFile(Base64Param param) throws Exception {
        Assert.notBlank(param.getFilePath(), "参数 filePath 不能为空");
        String bucketName = FileUtils.getRootByPath(param.getFilePath());
        String objectName = FileUtils.exceptRootByPath(param.getFilePath());
        Assert.notBlank(bucketName, "参数 bucketName 不能为空");
        Assert.notBlank(objectName, "参数 objectName 不能为空");
        if (Objects.isNull(param.getOriginName()) || !param.getOriginName()) {
            objectName = FileUtils.fileNameWithTime(objectName);
        }
        log.debug("[上传文件], bucketName = {}, objectName = {}", bucketName, objectName);

        boolean ret = base64StoreFile(param.getBase64(), bucketName, objectName);
        if (!ret) {
            log.warn("[上传失败] bucketName = {}, objectName = {}", bucketName, objectName);
            return null;
        }

        StringBuilder sb = new StringBuilder();
        return sb.append(StrUtil.C_SLASH).append(bucketName).append(objectName).toString();
    }

}
